home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
shells
/
zsh-3.0-p
/
zsh-3
/
zsh-3.0-pre3
/
Src
/
signals.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-05
|
19KB
|
755 lines
/*
* $Id: signals.c,v 2.13 1996/07/05 20:52:22 hzoli Exp $
*
* signals.c - signals handling code
*
* This file is part of zsh, the Z shell.
*
* Copyright (c) 1992-1996 Paul Falstad
* All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* In no event shall Paul Falstad or the Zsh Development Group be liable
* to any party for direct, indirect, special, incidental, or consequential
* damages arising out of the use of this software and its documentation,
* even if Paul Falstad and the Zsh Development Group have been advised of
* the possibility of such damage.
*
* Paul Falstad and the Zsh Development Group specifically disclaim any
* warranties, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose. The software
* provided hereunder is on an "as is" basis, and Paul Falstad and the
* Zsh Development Group have no obligation to provide maintenance,
* support, updates, enhancements, or modifications.
*
*/
#include "zsh.h"
/* signals that are trapped = 1, signals ignored = 2 */
int sigtrapped[VSIGCOUNT];
/* trap functions for each signal */
List sigfuncs[VSIGCOUNT];
/* This is only used on machines that don't understand signal sets. *
* On SYSV machines this will represent the signals that are blocked *
* (held) using sighold. On machines which can't block signals at *
* all, we will simulate this by ignoring them and remembering them *
* in this variable. */
#if !defined(POSIX_SIGNALS) && !defined(BSD_SIGNALS)
static sigset_t blocked_set;
#endif
#ifdef POSIX_SIGNALS
# define signal_jmp_buf sigjmp_buf
# define signal_setjmp(b) sigsetjmp((b),1)
# define signal_longjmp(b,n) siglongjmp((b),(n))
#else
# define signal_jmp_buf jmp_buf
# define signal_setjmp(b) setjmp(b)
# define signal_longjmp(b,n) longjmp((b),(n))
#endif
#ifdef NO_SIGNAL_BLOCKING
# define signal_process(sig) signal_ignore(sig)
# define signal_reset(sig) install_handler(sig)
#else
# define signal_process(sig) ;
# define signal_reset(sig) ;
#endif
/* Install signal handler for given signal. *
* If possible, we want to make sure that interrupted *
* system calls are not restarted. */
/**/
void
install_handler(int sig)
{
#ifdef POSIX_SIGNALS
struct sigaction act;
act.sa_handler = (SIGNAL_HANDTYPE) handler;
sigemptyset(&act.sa_mask); /* only block sig while in handler */
act.sa_flags = 0;
# ifdef SA_INTERRUPT /* SunOS 4.x */
if (interact)
act.sa_flags |= SA_INTERRUPT; /* make sure system calls are not restarted */
# endif
sigaction(sig, &act, (struct sigaction *)NULL);
#else
# ifdef BSD_SIGNALS
struct sigvec vec;
vec.sv_handler = (SIGNAL_HANDTYPE) handler;
vec.sv_mask = sigmask(sig); /* mask out this signal while in handler */
# ifdef SV_INTERRUPT
vec.sv_flags = SV_INTERRUPT; /* make sure system calls are not restarted */
# endif
sigvec(sig, &vec, (struct sigvec *)NULL);
# else
# ifdef SYSV_SIGNALS
/* we want sigset rather than signal because it will *
* block sig while in handler. signal usually doesn't */
sigset(sig, handler);
# else /* NO_SIGNAL_BLOCKING (bummer) */
signal(sig, handler);
# endif /* SYSV_SIGNALS */
# endif /* BSD_SIGNALS */
#endif /* POSIX_SIGNALS */
}
/* enable ^C interrupts */
/**/
void
intr(void)
{
if (interact)
install_handler(SIGINT);
}
/* disable ^C interrupts */
/**/
void
nointr(void)
{
if (interact)
signal_ignore(SIGINT);
}
/* temporarily block ^C interrupts */
/**/
void
holdintr(void)
{
if (interact)
signal_block(signal_mask(SIGINT));
}
/* release ^C interrupts */
/**/
void
noholdintr(void)
{
if (interact)
signal_unblock(signal_mask(SIGINT));
}
/* create a signal mask containing *
* only the given signal */
/**/
sigset_t
signal_mask(int sig)
{
sigset_t set;
sigemptyset(&set);
if (sig)
sigaddset(&set, sig);
return set;
}
/* Block the signals in the given signal *
* set. Return the old signal set. */
/**/
sigset_t
signal_block(sigset_t set)
{
sigset_t oset;
#ifdef POSIX_SIGNALS
sigprocmask(SIG_BLOCK, &set, &oset);
#else
# ifdef BSD_SIGNALS
oset = sigblock(set);
# else
# ifdef SYSV_SIGNALS
int i;
oset = blocked_set;
for (i = 1; i <= NSIG; ++i) {
if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
sigaddset(&blocked_set, i);
sighold(i);
}
}
# else /* NO_SIGNAL_BLOCKING */
/* We will just ignore signals if the system doesn't have *
* the ability to block them. */
int i;
oset = blocked_set;
for (i = 1; i <= NSIG; ++i) {
if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
sigaddset(&blocked_set, i);
signal_ignore(i);
}
}
# endif /* SYSV_SIGNALS */
# endif /* BSD_SIGNALS */
#endif /* POSIX_SIGNALS */
return oset;
}
/* Unblock the signals in the given signal *
* set. Return the old signal set. */
/**/
sigset_t
signal_unblock(sigset_t set)
{
sigset_t oset;
#ifdef POSIX_SIGNALS
sigprocmask(SIG_UNBLOCK, &set, &oset);
#else
# ifdef BSD_SIGNALS
sigfillset(&oset);
oset = sigsetmask(oset);
sigsetmask(oset & ~set);
# else
# ifdef SYSV_SIGNALS
int i;
oset = blocked_set;
for (i = 1; i <= NSIG; ++i) {
if (sigismember(&set, i) && sigismember(&blocked_set, i)) {
sigdelset(&blocked_set, i);
sigrelse(i);
}
}
# else /* NO_SIGNAL_BLOCKING */
/* On systems that can't block signals, we are just ignoring them. So *
* to unblock signals, we just reenable the signal handler for them. */
int i;
oset = blocked_set;
for (i = 1; i <= NSIG; ++i) {
if (sigismember(&set, i) && sigismember(&blocked_set, i)) {
sigdelset(&blocked_set, i);
install_handler(i);
}
}
# endif /* SYSV_SIGNALS */
# endif /* BSD_SIGNALS */
#endif /* POSIX_SIGNALS */
return oset;
}
/* set the process signal mask to *
* be the given signal mask */
/**/
sigset_t
signal_setmask(sigset_t set)
{
sigset_t oset;
#ifdef POSIX_SIGNALS
sigprocmask(SIG_SETMASK, &set, &oset);
#else
# ifdef BSD_SIGNALS
oset = sigsetmask(set);
# else
# ifdef SYSV_SIGNALS
int i;
oset = blocked_set;
for (i = 1; i <= NSIG; ++i) {
if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
sigaddset(&blocked_set, i);
sighold(i);
} else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) {
sigdelset(&blocked_set, i);
sigrelse(i);
}
}
# else /* NO_SIGNAL_BLOCKING */
int i;
oset = blocked_set;
for (i = 1; i < NSIG; ++i) {
if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
sigaddset(&blocked_set, i);
signal_ignore(i);
} else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) {
sigdelset(&blocked_set, i);
install_handler(i);
}
}
# endif /* SYSV_SIGNALS */
# endif /* BSD_SIGNALS */
#endif /* POSIX_SIGNALS */
return oset;
}
#if defined(NO_SIGNAL_BLOCKING)
static int suspend_longjmp = 0;
static signal_jmp_buf suspend_jmp_buf;
#endif
/**/
int
signal_suspend(int sig, int sig2)
{
int ret;
#ifdef POSIX_SIGNALS
sigset_t set;
sigfillset(&set);
sigdelset(&set, sig);
sigdelset(&set, SIGHUP); /* still don't know why we add this? */
if (sig2)
sigdelset(&set, sig2);
ret = sigsuspend(&set);
#else
# ifdef BSD_SIGNALS
sigset_t set;
sigfillset(&set);
sigdelset(&set, sig);
if (sig2)
sigdelset(&set, sig2);
ret = sigpause(set);
# else
# ifdef SYSV_SIGNALS
ret = sigpause(sig);
# else /* NO_SIGNAL_BLOCKING */
/* need to use signal_longjmp to make this race-free *
* between the child_unblock() and pause() */
if (signal_setjmp(suspend_jmp_buf) == 0) {
suspend_longjmp = 1; /* we want to signal_longjmp after catching signal */
child_unblock(); /* do we need to unblock sig2 as well? */
ret = pause();
}
suspend_longjmp = 0; /* turn off using signal_longjmp since we are past *
* the pause() function. */
# endif /* SYSV_SIGNALS */
# endif /* BSD_SIGNALS */
#endif /* POSIX_SIGNALS */
return ret;
}
/* Use a circular queue to save signals caught during *
* critical sections of code. You call queue_signals to *
* start queueing, and unqueue_signals to process the *
* queue and stop queueing. Since the kernel doesn't *
* queue signals, it is probably overkill for zsh to do *
* this, but it shouldn't hurt anything to do it anyway. */
/* Right now I'm queueing all signals, but maybe we only *
* need to queue SIGCHLD. Anybody know? */
#define MAX_QUEUE_SIZE 16
static int queueing_enabled;
static sigset_t signal_mask_queue[MAX_QUEUE_SIZE];
static int signal_queue[MAX_QUEUE_SIZE];
static int queue_front = 0;
static int queue_rear = 0;
/* Start queueing up any received signals rather *
* than handling them. */
/**/
void
queue_signals(void)
{
queueing_enabled++; /* signals are now being queued */
}
/* Process all queued signals */
/**/
void
unqueue_signals(void)
{
DPUTS(!queueing_enabled, "BUG: unqueue_signals called but not queueing");
if (--queueing_enabled)
return;
while (queue_front != queue_rear) { /* while signals in queue */
sigset_t oset;
queue_front = ++queue_front % MAX_QUEUE_SIZE;
oset = signal_setmask(signal_mask_queue[queue_front]);
handler(signal_queue[queue_front]); /* handle queued signal */
signal_setmask(oset);
}
}
/* What flavor of waitpid/wait3/wait shall we use? */
#ifdef HAVE_WAITPID
# define WAIT(pid, statusp, options) waitpid(pid, statusp, options)
#else
# ifdef HAVE_WAIT3
# define WAIT(pid, statusp, options) wait3((void *) statusp, options, NULL)
# else
# define WAIT(pid, statusp, options) wait(statusp)
# endif
#endif
/* the signal handler */
/**/
RETSIGTYPE
handler(int sig)
{
sigset_t newmask, oldmask;
#if defined(NO_SIGNAL_BLOCKING)
int do_jump;
signal_jmp_buf jump_to;
#endif
signal_process(sig);
sigfillset(&newmask);
oldmask = signal_block(newmask); /* Block all signals temporarily */
#if defined(NO_SIGNAL_BLOCKING)
do_jump = suspend_longjmp; /* do we need to longjmp to signal_suspend */
suspend_longjmp = 0; /* In case a SIGCHLD somehow arrives */
if (sig == SIGCHLD) { /* Traps can cause nested child_suspend() */
if (do_jump)
jump_to = suspend_jmp_buf; /* Copy suspend_jmp_buf */
}
#endif
if (queueing_enabled) { /* Are we queueing signals now? */
int temp_rear = ++queue_rear % MAX_QUEUE_SIZE;
DPUTS(temp_rear == queue_front, "BUG: signal queue full");
if (temp_rear != queue_front) { /* Make sure it's not full (extremely unlikely) */
queue_rear = temp_rear; /* ok, not full, so add to queue */
signal_queue[queue_rear] = sig; /* save signal caught */
signal_mask_queue[queue_rear] = oldmask; /* save current signal mask */
}
signal_reset(sig);
return;
}
signal_setmask(oldmask); /* Reset signal mask, signal traps ok now */
switch (sig) {
case SIGCHLD:
/* keep WAITING until no more child processes to reap */
for (;;)
cont: {
int old_errno = errno; /* save the errno, since WAIT may change it */
int status;
Job jn;
Process pn;
pid_t pid;
pid_t *procsubpid = &cmdoutpid;
int *procsubval = &cmdoutval;
struct execstack *es = exstack;
pid = WAIT(-1, &status, WNOHANG|WUNTRACED); /* reap the child process */
if (!pid) /* no more children to reap */
break;
/* check if child returned was from process substitution */
for (;;) {
if (pid == *procsubpid) {
*procsubpid = 0;
if (WIFSIGNALED(status)) {
*procsubval = (0200 | WTERMSIG(status));
if (WTERMSIG(status) == SIGINT)
kill(getpid(), SIGINT);
else if (sigtrapped[WTERMSIG(status)])
dotrap(WTERMSIG(status));
} else
*procsubval = WEXITSTATUS(status);
goto cont;
}
if (!es)
break;
procsubpid = &es->cmdoutpid;
procsubval = &es->cmdoutval;
es = es->next;
}
/* check for WAIT error */
if (pid == -1) {
if (errno != ECHILD)
zerr("wait failed: %e", NULL, errno);
errno = old_errno; /* WAIT changed errno, so restore the original */
break;
}
/* Find the process and job containing this pid and update it. */
if (findproc(pid, &jn, &pn)) {
update_process(pn, status);
update_job(jn);
}
}
break;
case SIGHUP:
if (sigtrapped[SIGHUP])
dotrap(SIGHUP);
else {
stopmsg = 1;
zexit(SIGHUP, 1);
}
break;
case SIGINT:
if (sigtrapped[SIGINT])
dotrap(SIGINT);
else {
extern int list_pipe, simple_pline;
if (list_pipe || chline || simple_pline) {
breaks = loops;
errflag = 1;
}
}
break;
case SIGWINCH:
adjustwinsize(); /* check window size and adjust */
break;
case SIGALRM:
if (sigtrapped[SIGALRM]) {
int tmout;
dotrap(SIGALRM);
if ((tmout = getiparam("TMOUT")))
alarm(tmout); /* reset the alarm */
} else {
int idle = ttyidlegetfn(NULL);
int tmout = getiparam("TMOUT");
if (idle >= 0 && idle < tmout)
alarm(tmout - idle);
else {
zerr("timeout", NULL, 0);
stopmsg = 1;
zexit(SIGALRM, 1);
}
}
break;
default:
dotrap(sig);
break;
} /* end of switch(sig) */
signal_reset(sig);
/* This is used to make signal_suspend() race-free */
#if defined(NO_SIGNAL_BLOCKING)
if (do_jump)
signal_longjmp(jump_to, 1);
#endif
} /* handler */
/* SIGHUP any jobs left running */
/**/
void
killrunjobs(int from_signal)
{
int i, killed = 0;
if (isset(NOHUP))
return;
for (i = 1; i < MAXJOB; i++)
if ((from_signal || i != thisjob) && (jobtab[i].stat & STAT_LOCKED) &&
!(jobtab[i].stat & STAT_NOPRINT) &&
!(jobtab[i].stat & STAT_STOPPED)) {
if (killpg(jobtab[i].gleader, SIGHUP) != -1)
killed++;
}
if (killed)
zerr("warning: %d jobs SIGHUPed", NULL, killed);
}
/* send a signal to a job (simply involves kill if monitoring is on) */
/**/
int
killjb(Job jn, int sig)
{
Process pn;
int err = 0;
if (jobbing) {
if (jn->stat & STAT_SUPERJOB) {
if (sig == SIGCONT) {
for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
kill(pn->pid, sig);
for (pn = jn->procs; pn->next; pn = pn->next)
err = kill(pn->pid, sig);
return err;
}
killpg(jobtab[jn->other].gleader, sig);
return killpg(jn->gleader, sig);
}
else
return (killpg(jn->gleader, sig));
}
for (pn = jn->procs; pn; pn = pn->next)
if ((err = kill(pn->pid, sig)) == -1 && errno != ESRCH)
return -1;
return err;
}
/**/
void
settrap(int sig, List l)
{
Cmd c;
if (l && l->left && l->left->left) {
c = l->left->left->left;
if (c && c->type == SIMPLE && empty(c->args) && empty(c->redir)
&& empty(c->vars) && !c->flags)
l = NULL;
}
if (sig == -1)
return;
if (jobbing && (sig == SIGTTOU || sig == SIGTSTP ||
sig == SIGTTIN || sig == SIGPIPE)) {
zerr("can't trap SIG%s in interactive shells", sigs[sig], 0);
return;
}
if (sigfuncs[sig])
freestruct(sigfuncs[sig]);
if (!l) {
sigtrapped[sig] = 2;
sigfuncs[sig] = NULL;
if (sig && sig <= SIGCOUNT &&
#ifdef SIGWINCH
sig != SIGWINCH &&
#endif
sig != SIGCHLD)
signal_ignore(sig);
} else {
if (sig && sig <= SIGCOUNT &&
#ifdef SIGWINCH
sig != SIGWINCH &&
#endif
sig != SIGCHLD)
install_handler(sig);
sigtrapped[sig] = 1;
PERMALLOC {
sigfuncs[sig] = (List) dupstruct(l);
} LASTALLOC;
}
}
/**/
void
unsettrap(int sig)
{
if (sig == -1)
return;
if (jobbing && (sig == SIGTTOU || sig == SIGTSTP ||
sig == SIGTTIN || sig == SIGPIPE)) {
return;
}
sigtrapped[sig] = 0;
if (sig == SIGINT && interact) {
/* PWS 1995/05/16: added test for interactive, also noholdintr()
* as subshells ignoring SIGINT have it blocked from delivery
*/
intr();
noholdintr();
} else if (sig == SIGHUP)
install_handler(sig);
else if (sig && sig <= SIGCOUNT &&
#ifdef SIGWINCH
sig != SIGWINCH &&
#endif
sig != SIGCHLD)
signal_default(sig);
if (sigfuncs[sig]) {
freestruct(sigfuncs[sig]);
sigfuncs[sig] = NULL;
}
}
/* Execute a trap function for a given signal */
/**/
void
dotrap(int sig)
{
LinkList args;
char *name, num[4];
int sav = sigtrapped[sig];
/* if signal is being ignored or the trap function *
* is NULL, then return *
* *
* Also return if errflag is set. In fact, the code in the *
* function will test for this, but this way we keep status flags *
* intact without working too hard. Special cases (e.g. calling *
* a trap for SIGINT after the error flag was set) are handled *
* by the calling code. (PWS 1995/06/08). */
if (sav == 2 || !sigfuncs[sig] || errflag)
return;
sigtrapped[sig] = 2;
lexsave();
execsave();
PERMALLOC {
args = newlinklist();
name = (char *) zalloc(5 + strlen(sigs[sig]));
sprintf(name, "TRAP%s", sigs[sig]);
addlinknode(args, name);
sprintf(num, "%d", sig);
addlinknode(args, num);
} LASTALLOC;
trapreturn = -1;
doshfunc(sigfuncs[sig], args, 0, 1);
execrestore();
lexrestore();
freelinklist(args, (FreeFunc) NULL);
zsfree(name);
if (trapreturn > 0) {
breaks = loops;
errflag = 1;
} else {
trapreturn = 0;
}
if (sigtrapped[sig])
sigtrapped[sig] = sav;
}